﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

namespace Developex.DatabaseWrapper.Secure
{
	/// <summary>
	/// 
	/// </summary>
	public class DESHelper
	{
		#region Parameters

		///<summary>
		/// key 1 for encryption/decryption
		///</summary>
		/// <remarks>
		/// highly recommended to change standard values in secure purposes in your application
		/// </remarks>
		public static byte[] Key = new byte[] {5, 155, 244, 26, 215, 206, 21, 219};

		///<summary>
		/// key 2 for encryption/decryption
		///</summary>
		/// <remarks>
		/// highly recommended to change standard values in secure purposes in your application
		/// </remarks>
		public static byte[] KeyIV = new byte[] { 34, 233, 101, 50, 222, 184, 195, 47 };

		/// <summary>
		/// excessive bytes count that will be added while encryption, ignored w
		/// </summary>
		/// <remarks>
		/// if greater than zero EncryptText will produce different result even no the same data for encryption
		/// </remarks>
		public static int ExtraBytesCount = 0;

		#endregion

		private static readonly Random _rand = new Random();

		/// <summary>
		/// Encrypts a string 
		/// </summary>
		/// <param name="sData">string to encrypt</param>
		/// <returns>Encrypted string</returns>
		static public string EncryptText(string sData)
		{
			return Encrypt(sData, Key, KeyIV, ExtraBytesCount);
		}

		/// <summary>
		/// Encrypts a string, ignore ExtraBytesCount
		/// </summary>
		/// <param name="sData">string to encrypt</param>
		/// <returns>Encrypted string</returns>
		static public string EncryptTextNoExtraBytes(string sData)
		{
			return Encrypt(sData, Key, KeyIV, 0);
		}

		/// <summary>
		/// Decrypts a string
		/// </summary>
		/// <param name="sData">encrpted string to decrypt</param>
		/// <returns>a decrypted string</returns>
		static public string DecryptText(string sData)
		{
			return Decrypt(sData, Key, KeyIV, ExtraBytesCount);
		}

		/// <summary>
		/// Decrypts a string, ignore ExtraBytesCount
		/// </summary>
		/// <param name="sData">encrpted string to decrypt</param>
		/// <returns>a decrypted string</returns>
		static public string DecryptTextNoExtraBytes(string sData)
		{
			return Decrypt(sData, Key, KeyIV, 0);
		}

		/// <summary>
		/// Encrypt string base function that takes the key
		/// </summary>
		/// <param name="sData">data to encrypt</param>
		/// <param name="key"></param>
		/// <param name="keyIV"></param>
		/// <param name="extraCount"></param>
		/// <returns>encrypted data in base64</returns>
		static public string Encrypt(string sData, byte[] key, byte[] keyIV, int extraCount)
		{
			if (sData == null)
				return null;

			byte[] byteData = Encoding.UTF8.GetBytes(sData);
			if (extraCount > 0)
			{
				byte[] extra = new byte[extraCount];
				lock (_rand)
				{
					_rand.NextBytes(extra);
				}
				int len = byteData.Length;
				Array.Resize(ref extra, extraCount + len);
				Array.Copy(byteData, 0, extra, extraCount, len);
				byteData = extra;
			}

			// create the provider
			DESCryptoServiceProvider desNow = new DESCryptoServiceProvider();

			// encrypt
			MemoryStream msNow = new MemoryStream();
			CryptoStream csNow = new CryptoStream(msNow, desNow.CreateEncryptor(key, keyIV), CryptoStreamMode.Write);
			csNow.Write(byteData, 0, byteData.Length);
			csNow.FlushFinalBlock();

			return Convert.ToBase64String(msNow.ToArray());
		}

		/// <summary>
		/// Decrypt string base function that takes a key
		/// </summary>
		/// <param name="sData">data to decrypt in base64</param>
		/// <param name="key"></param>
		/// <param name="keyIV"></param>
		/// <param name="extraCount"></param>
		/// <returns>decrypted text</returns>
		static public string Decrypt(string sData, byte[] key, byte[] keyIV, int extraCount)
		{
			if (sData == null)
				return null;

			byte[] byteData = Convert.FromBase64String(sData);

			// create the provider
			DESCryptoServiceProvider desNow = new DESCryptoServiceProvider();

			// decrypt
			MemoryStream msNow = new MemoryStream();
			CryptoStream cryptoStream = new CryptoStream(msNow, desNow.CreateDecryptor(key, keyIV), CryptoStreamMode.Write);
			cryptoStream.Write(byteData, 0, byteData.Length);
			cryptoStream.FlushFinalBlock();
			byte[] buff = msNow.ToArray();
			msNow.Close();
			return Encoding.UTF8.GetString(buff, extraCount, buff.Length - extraCount);
		}
	}
}
